#include <sys/sem.h>
#include <unistd.h>
#include <wait.h>

#include <cstdlib>
#include <cstring>
#include <iostream>

using namespace std;

// USE FOR SYSTEM V SEMAPHORES

union semun {
  int val;                // 设置信号量值
  struct semid_ds *buf;   // 设置信号集属性
  unsigned short *array;  // 设置信号量数组
  struct seminfo *buff;   // 设置信号量信息
};

#define USE
void pv(int sem_id, short ops) {
#ifdef USE
  struct sembuf buf = {
      .sem_num = 0,   // 信号集中信号量的编号(0代表第1个信号量)
      .sem_op = ops,  // 操作值(P:<0 V:>0)
      .sem_flg = SEM_UNDO  // 信号量的默认操作(避免因为进程异常退出而造成的死锁)
  };
  // 操作信号量(PV)
  // 参数：信号量ID，操作数组，操作数组长度
  semop(sem_id, &buf, 1);
#endif
}

int main(int argc, char const *argv[]) {
  // 创建信号量集
  // 参数：IPC_PRIVATE(创建一个私有的信号量集，不与其他进程共享)、信号量集数量、权限
  int sem_id = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT);
  if (sem_id == -1) {
    perror("Error For semget");
    exit(1);
  }
  union semun sem_un;
  sem_un.val = 1;
  // 用联合体中val成员的值设置信号量集合中单个信号量的值
  // 参数：信号量集ID、信号量序号、操作类型、操作值
  semctl(sem_id, 0, SETVAL, sem_un);

  pid_t pid = fork();
  if (pid < 0) {
    cerr << "fork error" << endl;
    exit(1);
  } else if (pid == 0) {
    pv(sem_id, -1);  // P操作
    /*** 临界区 ***/
    int i = 5;
    while (i--) cout << "[SUB] THIS SUB PROC" << endl;  // 抢占式输出
    /*** 临界区 ***/
    pv(sem_id, 1);  // V操作
    exit(0);
  }

  pv(sem_id, -1);
  /*** 临界区 ***/
  int i = 5;
  while (i--) cout << "[MAIN] THIS MAIN PROC" << endl;
  /*** 临界区 ***/
  pv(sem_id, 1);

  wait(NULL);
  // 删除信号量集
  semctl(sem_id, 0, IPC_RMID, 0);
  return 0;
}
